﻿using System;
using System.Collections;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;

namespace Streambolics
{
    [Serializable]
    public class ConfigurationBuilder
    {
        private XmlDocument _ConfigurationFile = new XmlDocument ();
        private static Regex _typeParsePattern = new Regex (@"([^,]+),");

        #region Constructors

        public ConfigurationBuilder ()
        {
        }

        public ConfigurationBuilder (string AInitialFileName)
        {
            Add (AInitialFileName);
        }

        #endregion

        public void Add (string AFileName)
        {
            Add (new FileStream (AFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
        }

        public void Add (FileStream AFile)
        {
            XmlDocument d = new XmlDocument ();

            d.Load (AFile);

            if (_ConfigurationFile == null)
            {
                _ConfigurationFile = d;
            }
            else
            {
                Merge (d, _ConfigurationFile);
            }
        }

        public void Save (string saveAsname)
        {
            _ConfigurationFile.Save (saveAsname);
        }

        #region Helper functions

        public static void Merge (XmlDocument ADocument, XmlDocument ATarget)
        {
            RecursiveMerge (ADocument.ChildNodes, ATarget);
        }

        private static void RecursiveMerge (XmlNodeList AAdditionalNodes, XmlNode ATarget)
        {
            int RepeatedElementIndex = 0;
            XmlNode PreviousElement = null;

            foreach (XmlNode n in AAdditionalNodes)
            {
                if (n.NodeType != XmlNodeType.Element)
                {
                    continue;
                }

                if (PreviousElement != null && n.Name == PreviousElement.Name && n.NamespaceURI == PreviousElement.NamespaceURI)
                {
                    RepeatedElementIndex++;
                }
                else
                {
                    RepeatedElementIndex = 0;
                }

                PreviousElement = n;

                XmlNode toNode;
                if (n.Attributes["key"] != null)
                {
                    toNode = Locate (ATarget, n, n.Attributes["key"]);
                }
                else if (n.Attributes["name"] != null)
                {
                    toNode = Locate (ATarget, n, n.Attributes["name"]);
                }
                else if (n.Attributes["type"] != null)
                {
                    toNode = Locate (ATarget, n, n.Attributes["type"]);
                }
                else
                {
                    toNode = Locate (ATarget, n, RepeatedElementIndex);
                }

                if (toNode == null)
                {
                    Debug.Assert (n != null);
                    Debug.Assert (n.Name != null);
                    Debug.Assert (ATarget != null);
                    Debug.Assert (ATarget.OwnerDocument != null);
                    Debug.Assert (n.ParentNode.Name == ATarget.Name);
                    toNode = ATarget.AppendChild (ATarget.OwnerDocument.CreateElement (n.Name));
                }

                XmlNode textEl = GetTextElement (n);
                if (textEl != null)
                {
                    toNode.InnerText = textEl.InnerText;
                }

                foreach (XmlAttribute attrib in n.Attributes)
                {
                    XmlAttribute toAttrib = toNode.Attributes[attrib.Name];
                    if (toAttrib == null)
                    {
                        toAttrib = toNode.Attributes.Append (toNode.OwnerDocument.CreateAttribute (attrib.Name));
                    }
                    toAttrib.InnerText = attrib.InnerText;
                }
                ((XmlElement)toNode).IsEmpty = !toNode.HasChildNodes; 
                RecursiveMerge (n.ChildNodes, toNode);
            }
        }

        private static XmlNode GetTextElement (XmlNode node)
        {
            foreach (XmlNode subNode in node.ChildNodes)
            {
                if (subNode.NodeType == XmlNodeType.Text)
                {
                    return subNode;
                }
            }

            return null;
        }

        private static XmlNode Locate (XmlNode ATree, XmlNode ASearchPattern, int AIndex)
        {
            int Count = -1;

            foreach (XmlNode n in ATree.ChildNodes)
            {
                if (n.Name != ASearchPattern.Name || n.NamespaceURI != ASearchPattern.NamespaceURI)
                {
                    continue;
                }

                if (++Count == AIndex)
                {
                    return n;
                }
                else
                {
                    continue;
                }

            }

            return null;
        }

        private static XmlNode Locate (XmlNode ATree, XmlNode ASearchPattern, XmlAttribute AKeyAttribute)
        {
            XmlNode BestMatch = null;

            foreach (XmlNode n in ATree.ChildNodes)
            {
                if (n.Name != ASearchPattern.Name || n.NamespaceURI != ASearchPattern.NamespaceURI)
                {
                    continue;
                }

                if (n.Attributes[AKeyAttribute.Name] != null && n.Attributes[AKeyAttribute.Name].InnerText == AKeyAttribute.InnerText)
                {
                    BestMatch = n;
                }
                else if (AKeyAttribute.Name == "type")
                {
                    Match subNodeMatch = _typeParsePattern.Match (n.Attributes[AKeyAttribute.Name].InnerText);
                    Match keyAttribMatch = _typeParsePattern.Match (AKeyAttribute.InnerText);
                    if (subNodeMatch.Success && keyAttribMatch.Success && subNodeMatch.Result ("$1") == keyAttribMatch.Result ("$1"))
                    {
                        BestMatch = n;
                    }
                }
            }

            return BestMatch;
        }

        #endregion

    }
	
	
}
